#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <linux/fb.h>

#define DISPLAY_TO_FILE           0 //写入文件
#define DISPLAY_TO_FRAMEBUFFER    1 //写入帧缓冲区
#define WHERE_TO_DISPLAY          DISPLAY_TO_FILE 

char *fb;

void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) 
{
#if WHERE_TO_DISPLAY==DISPLAY_TO_FRAMEBUFFER 
		char* p=fb;
		int i,j;
		for(i=0;i<768;i++)
		{
				for(j=0;j<1360;j++)
				{
						*p++ = i*pFrame->linesize[0] + pFrame->data[0];
						*p++ = i*pFrame->linesize[0] + pFrame->data[1];
						*p++ = i*pFrame->linesize[0] + pFrame->data[2];
						p++;
				}
		}
#elif WHERE_TO_DISPLAY==DISPLAY_TO_FILE 
  FILE *pFile;
  char szFilename[32];
  int  y;
 
  // Open file
  sprintf(szFilename, "/tmp/frame%d.ppm", iFrame);
  //printf("写文件: %s\n", szFilename);
  pFile=fopen(szFilename, "wb");

  if(pFile==NULL)
	{
			printf("打开文件错误\n");
    	return;
 	}

  // Write header
  fprintf(pFile, "P6\n%d %d\n255\n", width, height);
 
  // Write pixel data
  for(y=0; y<height; y++)
    fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
 
  // Close file
  fclose(pFile);
	//printf("写文件完毕\n");
#endif
}

int main(int argc, char *argv[])  
{  
    AVFormatContext *pFormatCtx;  
    int             i, videoStream;  
    AVCodecContext  *pCodecCtx;  
    AVCodec         *pCodec;  
    AVFrame         *pFrame;   
    AVFrame         *pFrameRGB;  
    AVPacket        packet;  
    int             frameFinished;  
    int             numBytes;  
    uint8_t         *buffer;  
    struct SwsContext *img_convert_ctx;  
  
		int fbdev;
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    long int screensize = 0;

    if(argc < 2)  
    {  
        printf("Please provide a movie file\n");  
        return -1;  
    }  
    // Register all formats and codecs  
    // 初始化ffmpeg库  
    av_register_all();  
  
    // Open video file  
    if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)  
        return -1; // Couldn't open file  
  
    // Retrieve stream information  
    // 查找文件的流信息  
    if(av_find_stream_info(pFormatCtx)<0)  
        return -1; // Couldn't find stream information  
  
    // Dump information about file onto standard error  
    // dump只是一个调试函数，输出文件的音、视频流的基本信息：帧率、分辨率、音频采样等等  
    dump_format(pFormatCtx, 0, argv[1], 0);  
  
    // Find the first video stream  
    // 遍历文件的流，找到第一个视频流，并记录流的编码信息  
    videoStream=-1;  
    for(i=0; i<pFormatCtx->nb_streams; i++)  
    {  
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)  
        {  
            videoStream=i;  
            break;  
        }  
    }  

    if(videoStream==-1)  
    {
        printf("not find any video stream!\n");
        return -1; // Didn't find a video stream  
    }

    // Get a pointer to the codec context for the video stream  
    // 得到视频流编码的上下文指针  
    pCodecCtx=pFormatCtx->streams[videoStream]->codec;  
  
    printf("pCodecCtx->codec_name=%s\n", pCodecCtx->codec_name);
    printf("pCodecCtx->codec_id=0x%x, %d, CODEC_ID_MJPEG=%d\n", pCodecCtx->codec_id, pCodecCtx->codec_id, CODEC_ID_MJPEG);


    // construct the scale context, conversing to PIX_FMT_RGB24  
    // 根据编码信息设置渲染格式  
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,   
            1366, 768, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);  
    if(img_convert_ctx == NULL)  
    {  
        fprintf(stderr, "Cannot initialize the conversion context!\n");  
//      exit(1);  
        return -1;  
    }  
  
    printf("%s: aaaaaaaaaa\n", __FUNCTION__);

    // Find the decoder for the video stream  
    // 在库里面查找支持该格式的解码器  
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);  
    if(pCodec==NULL)  
    {  
        fprintf(stderr, "Unsupported codec!\n");  
        return -1; // Codec not found  
    }  
    // Open codec  
    // 打开解码器  
    if(avcodec_open(pCodecCtx, pCodec)<0)  
        return -1; // Could not open codec  
  
    // Allocate video frame  
    // 分配一个帧指针，指向解码后的原始帧  
    pFrame=avcodec_alloc_frame();  
  
    // Allocate an AVFrame structure  
    // 分配一个帧指针，指向存放转换成rgb后的帧  
    pFrameRGB=avcodec_alloc_frame();  
    if(pFrameRGB==NULL)  
        return -1;  
  
    // Determine required buffer size and allocate buffer  
    numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);  
    buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));  // buffer = new uint8_t[numBytes];  
  
    // Assign appropriate parts of buffer to image planes in pFrameRGB  
    // Note that pFrameRGB is an AVFrame, but AVFrame is a superset  
    // of AVPicture  
    // 给pFrameRGB帧附加上分配的内存  
    avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);  
      printf("%s: eeeeeeeeeee\n", __FUNCTION__);

#if WHERE_TO_DISPLAY==DISPLAY_TO_FRAMEBUFFER 
		fbdev=open("/dev/fb0", O_RDWR);

    if (fbdev == -1) {
        perror("Error: cannot open framebuffer device");
        exit(1);
    }
    printf("The framebuffer device was opened successfully.\n");

    // Get fixed screen information
    if (ioctl(fbdev, FBIOGET_FSCREENINFO, &finfo) == -1) {
        perror("Error reading fixed information");
        exit(2);
    }

    // Get variable screen information
    if (ioctl(fbdev, FBIOGET_VSCREENINFO, &vinfo) == -1) {
        perror("Error reading variable information");
        exit(3);
    }

    printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

    // Figure out the size of the screen in bytes
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;


    fb=(short *)mmap(0, screensize, PROT_WRITE, MAP_SHARED, fbdev, 0);

    if ((int)fb == -1) {
        perror("Error: failed to map framebuffer device to memory");
        exit(4);
    }
    printf("The framebuffer device was mapped to memory successfully.\n");

    fprintf(stderr, "fb opened at %x, fd %d\n", fb, fbdev);
#endif


#if 0
    int x = 500; int y = 100;       // Where we are going to put the pixel
    int location = 0;
    // Figure out where in memory to put the pixel
    for (y = 100; y < 300; y++)
        for (x = 300; x < 500; x++) {

            location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
                       (y+vinfo.yoffset) * finfo.line_length;

            if (vinfo.bits_per_pixel == 32) {
                *(fb + location) = 100;        // Some blue
                *(fb + location + 1) = 15+(x-100)/2;     // A little green
                *(fb + location + 2) = 200-(y-100)/5;    // A lot of red
                *(fb + location + 3) = 0;      // No transparency
        //location += 4;
            } else  { //assume 16bpp
                int b = 10;
                int g = (x-100)/6;     // A little green
                int r = 31-(y-100)/16;    // A lot of red
                unsigned short int t = r<<11 | g << 5 | b;
                *((unsigned short int*)(fb + location)) = t;
            }

        }
#endif
    //while(1);

    // Read frames and save first five frames to disk  
    i=0;  
    while(av_read_frame(pFormatCtx, &packet)>=0) // 读取一个帧  
    {  
        //printf("%s: fffffffff\n", __FUNCTION__);
        // Is this a packet from the video stream?  
        if(packet.stream_index==videoStream)  
        {  
            // Decode video frame  
            // 解码该帧  
            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size);  
            printf("%s: 解码第%03d帧\n", __FUNCTION__, i);
            // Did we get a video frame?  
            if(frameFinished)  
            {  
                // Convert the image from its native format to RGB  
                // img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,   
                //              (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width,   
                //              pCodecCtx->height);  
                  
                // 把该帧转换成rgb  
  
                // 如果只提取关键帧，加上这句  
                // if (pFrame->key_frame == 1)  
                sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);    
                //sws_scale(img_convert_ctx, pFrameRGB->data, pFrameRGB->linesize, 0, pCodecCtx->height, pFrame->data, pFrame->linesize);    
                i++;
                // Save the frame to disk  
                // 保存前5帧  
                //if(++i<=5)  
                {  
                  //char pic[200];  
                  //sprintf(pic,"pic_%d.bmp",i);  
                  //av_create_bmp(pic, pFrameRGB->data[0], pCodecCtx->width, pCodecCtx->height, 24);  
                  SaveFrame(pFrameRGB, 1366, 768, i);  
                  //SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);  
#if 0
                  for(int x=0;x<vinfo.xres;x++)
                  {
                      for(int y=0;y<vinfo.yres;y++)
                      {
                          for(int z=0;z<3;z++)
                          {
                              fb[x*vinfo.xres + y*vinfo.yres + z] = 0xAA;
                          }
                      }
                  }

                  memcpy(fb, pFrameRGB->data, pCodecCtx->width * pCodecCtx->height*3);  
                  usleep(1000*1000);
#endif
                }  
          
            }  
        }  
  
        // Free the packet that was allocated by av_read_frame  
        // 释放读取的帧内存  
        av_free_packet(&packet);  
    }  
  
    // Free the RGB image  
    av_free(buffer);  
    av_free(pFrameRGB);  
  
    // Free the YUV frame  
    av_free(pFrame);  
  
    // Close the codec  
    avcodec_close(pCodecCtx);  
  
    // Close the video file  
    av_close_input_file(pFormatCtx);  
  
    return 0;  
}  



#if 0
static int av_create_bmp(AVFrame *pRGBBuffer,int width,int height,int bpp)
{
   BITMAPFILEHEADER bmpheader;
   BITMAPINFO bmpinfo;
   FILE *fp;
  FILE *pFile;
  char szFilename[32];
  int  y;
 
  // Open file
  sprintf(szFilename, "frame%d.bmp", bpp);
   fp = fopen(szFilename, "wb");
   if(!fp)return -1;
   bmpheader.bfType = ('M'<<8)|'B';
   bmpheader.bfReserved1 = 0;
   bmpheader.bfReserved2 = 0;
   bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
   bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;
   /*----注意，这里的bmpinfo.bmiHeader.biHeight变量的正负决定bmp文件的存储方式，如果
   为负值，表示像素是倒过来的*/
   bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
   bmpinfo.bmiHeader.biWidth = width;
   bmpinfo.bmiHeader.biHeight = -height;
   bmpinfo.bmiHeader.biPlanes = 1;
   bmpinfo.bmiHeader.biBitCount = 24;
   bmpinfo.bmiHeader.biCompression = BI_RGB;
   bmpinfo.bmiHeader.biSizeImage = 0;
   bmpinfo.bmiHeader.biXPelsPerMeter = 100;
   bmpinfo.bmiHeader.biYPelsPerMeter = 100;
   bmpinfo.bmiHeader.biClrUsed = 0;
   bmpinfo.bmiHeader.biClrImportant = 0;
   fwrite(&bmpheader,sizeof(BITMAPFILEHEADER),1,fp);
   fwrite(&bmpinfo.bmiHeader,sizeof(BITMAPINFOHEADER),1,fp);
//   fwrite(pRGBBuffer,width*height*bpp/8,1,fp);
     for(y=0; y<height; y++)
    fwrite(pRGBBuffer->data[0]+y*pRGBBuffer->linesize[0], 1, width*3, fp);
   fclose(fp);
   return 0;
}
#endif 

